package org.tlang.context;

import org.tlang.exception.EvalException;

import java.util.HashMap;
import java.util.Map;

public class SymbolTable {
    private final SymbolTable outer;
    private final Map<String, Integer> symbols;
    private final String tag; // for debug

    public SymbolTable(SymbolTable outer, String tag) {
        this.outer = outer;
        this.symbols = new HashMap<>();
        this.tag = tag;
    }

    public SymbolTable() {
        this(null, "anonymous");
    }

    public int size() {
        return this.symbols.size();
    }

    public void append(SymbolTable symbolTable) {
        symbols.putAll(symbolTable.symbols);
    }

    public Integer get(String symbol) {
        return symbols.get(symbol);
    }

    private Location where(String symbol, int nest) {
        Integer index = symbols.get(symbol);
        if (index == null) {
            if (outer == null) {
                return null;
            } else {
                return outer.where(symbol, nest + 1);
            }
        } else {
            return new Location(nest, index);
        }
    }

    public Location where(String symbol) {
        return where(symbol, 0);
    }

    /**
     * add only, if exist will throw exception
     *
     * @param symbol symbol
     * @return location
     */
    public int add(String symbol) {
        if (symbols.get(symbol) != null) {
            throw new EvalException("<" + symbol + "> is defined");
        }

        int index = symbols.size();
        symbols.put(symbol, index);
        return index;
    }

    /**
     * add or update
     *
     * @param symbol symbol
     * @return location
     */
    public int put(String symbol) {
        Integer index = symbols.get(symbol);
        if (index != null) {
            return index;
        }

        return add(symbol);
    }

    public SymbolTable outer() {
        return outer;
    }

    @Override
    public String toString() {
        return tag;
    }
}
